package br.org.bertol.mestrado.engine;

import java.util.ArrayList;
import java.util.List;

import br.org.bertol.mestrado.Verify;
import br.org.bertol.mestrado.engine.fitness.AbstractCombinatorialFitness;
import br.org.bertol.mestrado.engine.fitness.MutantAnalysis;
import br.org.bertol.mestrado.engine.optimisation.entity.Particle;
import br.org.bertol.mestrado.engine.optimisation.entity.Path;
import br.org.bertol.mestrado.engine.optimisation.pso.LinearInertiaReduction;

/**
 * @author contaqualquer
 * @param <T>
 *            Tipo de objeto
 */
public class RandomSolution<T> {

    /**
     */
    private Verify<T>              verify;

    /***/
    private ArrayList<Path>        randomPaths;

    /***/
    private ArrayList<Particle<T>> randomParticles;

    /***/
    public RandomSolution() {
    }

    public final void setVerify(final Verify<T> sa) {
        verify = sa;
    }

    /**
     * Gera solução aleatório de caminhos.
     * @param maxRandomSolutions
     *            Máximo de soluções aleatórias
     * @return Lista
     */
    @SuppressWarnings("unchecked")
    public final List<Path> generateRandomHillCimbingSolutions(
            final int maxRandomSolutions) {

        randomPaths = new ArrayList<Path>(maxRandomSolutions);

        for (int i = 0; i < maxRandomSolutions; i++) {
            final Path randomPath = new Path(verify.getObjectives());

            int maxIterNonAssig = 0;

            while (true) {
                final Double c = Double.valueOf(Math.random()
                        * verify.getClasses().size());

                int randomClass = c.intValue() < 1 ? 1 : c.intValue();

                if (randomPath.addClass(randomClass)) {
                    if (!randomPath.calcFitness()) {
                        randomPath.removeClass(randomClass);
                    }
                    if (randomPath.getRoute().size() == verify.getClasses()
                            .size() - 1) {
                        for (T fc : verify.getClasses()) {
                            randomPath.addClass((Integer) fc);
                        }
                        randomPaths.add(randomPath);
                        break;
                    }
                } else if (maxIterNonAssig > 100) {

                    boolean stop = false;

                    while (!stop) {
                        for (T cls : verify.getClasses()) {
                            if (randomPath.addClass((Integer) cls)) {

                                if (!randomPath.calcFitness()) {
                                    randomPath.removeClass((Integer) cls);
                                } else if (randomPath.getRoute().size() == verify
                                        .getClasses().size()) {
                                    randomPaths.add(randomPath);

                                    stop = true;

                                    break;
                                }
                            }
                        }
                    }
                    break;
                } else {
                    maxIterNonAssig++;
                }
            }
        }

        // inicia part�culas com valores aleat�rios
        for (Path path : randomPaths) {

            for (Objective objective : Objective.values()) {
                path.setHeight(objective, ((int) (Integer.MAX_VALUE * Math
                        .random())));
            }
        }

        return (List<Path>) randomPaths.clone();
    }

    /**
     * Gera soluções aleatório da PSO.
     * @param maxRandomSolutions
     *            Máximo de soluções iniciais
     * @param linearInertiaReduction
     *            Fator de redução linear
     * @param cognitivo
     *            Fator cognitivo
     * @param social
     *            Fator social
     * @return Lista de soluções
     */
    @SuppressWarnings("unchecked")
    public final List<Particle<T>> generateRandomPSOSolutions(
            final int maxRandomSolutions,
            final LinearInertiaReduction linearInertiaReduction,
            final float cognitivo, final float social) {

        randomParticles = new ArrayList<Particle<T>>(maxRandomSolutions);

        for (int i = 0; i < maxRandomSolutions; i++) {

            final MutantAnalysis fitness = new MutantAnalysis(
                    (ArrayList<String>) verify.getClasses());

            final Particle<T> randomParticle = new Particle<T>(
                    linearInertiaReduction, cognitivo, social,
                    (AbstractCombinatorialFitness<T>) fitness, verify
                    .getObjectives());

            int maxIterNonAssig = 0;

            // while (true) {
            for (int counter = 0; counter < 6; counter++) {

                final Double item = Double.valueOf(Math.random()
                        * (verify.getClasses().size()));

                final T randomClass = verify.getClasses()
                        .get((int) Math.floor(item));

                if (randomParticle.addClass(randomClass)) {

                    if (!randomParticle.calcFitness()) {
                        randomParticle.removeClass(randomClass);
                    }
                    if (randomParticle.getRoute().size() == verify.getClasses()
                            .size() - 1) {
                        for (T fc : verify.getClasses()) {
                            randomParticle.addClass(fc);
                        }
                        randomParticles.add(randomParticle);
                        break;
                    }
                } else if (maxIterNonAssig > 100) {

                    boolean stop = false;

                    while (!stop) {
                        for (T cls : verify.getClasses()) {
                            if (randomParticle.addClass(cls)) {
                                if (!randomParticle.calcFitness()) {
                                    randomParticle.removeClass((Integer) cls);
                                } else if (randomParticle.getRoute().size() == verify
                                        .getClasses().size()) {
                                    randomParticles.add(randomParticle);

                                    stop = true;

                                    break;
                                }
                            }
                        }
                    }
                    break;
                } else {
                    maxIterNonAssig++;
                }

                randomParticles.add(randomParticle);
            }
        }

        // inicia part�culas com valores aleat�rios
        for (Particle particle : randomParticles) {

            for (Objective objective : verify.getObjectives()) {
                particle.setHeight(objective, (int) (Integer.MAX_VALUE * Math
                        .random()));
            }

            particle.updatePBest();

            particle.updateGbest();
        }

        return (List<Particle<T>>) randomParticles.clone();
    }
}
